const { defineConfig } = require('@vue/cli-service')
const fs = require('fs');
const { SourceMapConsumer, SourceMapGenerator } = require('source-map');

module.exports = defineConfig({
  transpileDependencies: true,
  configureWebpack: {
    devtool: 'source-map',
    plugins: [{
      apply(compiler) {
        compiler.hooks.thisCompilation.tap('Initializing Compilation', (compilation) => {
          compilation.hooks.finishModules.tapPromise('All Modules Built', async (modules) => {
            for (const module of modules) {
              if (shouldSkipModule(module)) continue;

              const pathWithoutQuery = module.resource.replace(/\?.*$/, '');
              const sourceFile = fs.readFileSync(pathWithoutQuery).toString('utf-8');
              const sourceMap = extractSourceMap(module);

              sourceMap.sources = [pathWithoutQuery];
              sourceMap.sourcesContent = [sourceFile];
              sourceMap.mappings = await shiftMappings(sourceMap, sourceFile, pathWithoutQuery);
            }
          });
        });
      }
    }]
  },
  devServer:{
    // 解决跨域
    proxy:{
      '/api':{
        // localhost、127.0.0.1、公网地址三者都可以使用
        target: 'http://localhost:8082', // 后端接口地址
        // 允许跨域
        changeOrigin: true,
        // ws: true,
        pathRewrite: {
            '^/api': '/'
        }
      }
    }
  }
})

// ---------------------- 兼容Vue CLI 5.x 版本调试问题 ----------------------

function shouldSkipModule(module) {
  const { resource = '' } = module;

  if (!resource) return true;
  if (/node_modules/.test(resource)) return true;
  if (!/\.vue/.test(resource)) return true;
  if (!/type=script/.test(resource)) return true;
  if (!/lang=ts/.test(resource)) return true;
  if (isMissingSourceMap(module)) return true;

  return false;
}

function isMissingSourceMap(module) {
  return !extractSourceMap(module);
}

function extractSourceMap(module) {
  if (!module['_source']) return null;

  return module['_source']['_sourceMap'] ||
    module['_source']['_sourceMapAsObject'] ||
    null;
}

async function shiftMappings(sourceMap, sourceFile, sourcePath) {
  const indexOfScriptTag = getIndexOfScriptTag(sourceFile);

  const shiftedSourceMap = await SourceMapConsumer.with(sourceMap, null, async (consumer) => {
    const generator = new SourceMapGenerator();

    consumer.eachMapping((mapping) => {
      const {
        generatedColumn,
        generatedLine,
        originalColumn,
        originalLine
      } = mapping;

      let name = mapping.name;
      let source = sourcePath;

      var original = null

      if (originalLine === null || originalColumn === null) {
        name = null;
        source = null;
      }
      else {
        original = {
          column: originalColumn,
          line: originalLine + indexOfScriptTag,
        };
      }

      generator.addMapping({
        generated: {
          column: generatedColumn,
          line: generatedLine,
        },
        original,
        source,
        name
      });
    });

    return generator.toJSON();
  });

  return shiftedSourceMap.mappings;
}

function getIndexOfScriptTag(sourceFile) {
  const lines = sourceFile.match(/.+/g);
  let indexOfScriptTag = 0;

  for (const line of lines) {
    ++indexOfScriptTag;
    if (/<script/.test(line)) break;
  }

  return indexOfScriptTag;
}
